
/**
 &i2c0 {
      extgpio: sn7325@58 {
          compatible = "sn7325";
          reg = <0x58>;
          gpio-controller;
          #gpio-cells = <2>;
      };
 };




 */

#include <linux/module.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/string.h>
#include <linux/gpio.h>
#include <linux/interrupt.h>
#include <linux/i2c.h>
//#include <linux/i2c/sn732x.h>
#include <linux/of.h>

#define PORT_NONE	0x0	/* '/' No Port */
#define PORT_OUTPUT	0x1	/* 'O' Push-Pull, Output Only */
#define PORT_INPUT	0x2	/* 'I' Input Only */
#define PORT_OPENDRAIN	0x3	/* 'P' Open-Drain, I/O */

#define IO_4I4O		0x5AA5	/* O7 O6 I5 I4 I3 I2 O1 O0 */
#define IO_4P4O		0x5FF5	/* O7 O6 P5 P4 P3 P2 O1 O0 */
#define IO_8I		0xAAAA	/* I7 I6 I5 I4 I3 I2 I1 I0 */
#define IO_8P		0xFFFF	/* P7 P6 P5 P4 P3 P2 P1 P0 */
#define IO_8O		0x5555	/* O7 O6 O5 O4 O3 O2 O1 O0 */

#define GROUP_A(x)	((x) & 0xffff)	/* I2C Addr: 0b'110xxxx */
#define GROUP_B(x)	((x) << 16)	/* I2C Addr: 0b'101xxxx */

#define INT_NONE	0x0	/* No interrupt capability */
#define INT_NO_MASK	0x1	/* Has interrupts, no mask */
#define INT_INDEP_MASK	0x2	/* Has interrupts, independent mask */
#define INT_MERGED_MASK 0x3	/* Has interrupts, merged mask */

#define INT_CAPS(x)	(((uint64_t)(x)) << 32)
#define SN7325_I2C_SPEED				(100 * 1000)
enum {
	SN7319,
	SN7320,
	SN7321,
	SN7322,
	SN7323,
	SN7324,
	SN7325,
	SN7326,
	SN7327,
};


struct sn732x_platform_data {
	/* number of the first GPIO */
	unsigned	gpio_base;

	/* interrupt base */
	int  irq_base;

	void *context;	/* param to setup/teardown */

	int	 (*setup)(struct i2c_client *client,
				unsigned gpio, unsigned ngpio,
				void *context);
	int	 (*teardown)(struct i2c_client *client,
				unsigned gpio, unsigned ngpio,
				void *context);
};

static const struct i2c_device_id sn732x_id[] = {
	{ "sn7325", SN7325 },
	{ },
};
MODULE_DEVICE_TABLE(i2c, sn732x_id);

#ifdef CONFIG_OF
static const struct of_device_id sn732x_of_table[] = {
	{ .compatible = "sn7325" },
	{ }
};
MODULE_DEVICE_TABLE(of, sn732x_of_table);
#endif

struct sn732x_chip {
	struct gpio_chip gpio_chip;
	struct i2c_client *client;	/* "main" client */
	u8 reg_in[2];
	u8 reg_out[2];
	u8 reg_conf[2];
	u8 reg_int[2];
	int dir_input;
	int dir_output;
	struct mutex	lock;
};

static int debug_enable = 0;
module_param(debug_enable,int, 0644);

static inline struct sn732x_chip *to_sn732x(struct gpio_chip *gc)
{
	return container_of(gc, struct sn732x_chip, gpio_chip);
}

static int sn732x_writeb(struct sn732x_chip *chip, uint8_t reg, uint8_t val)
{
	struct i2c_client *client;
	int ret;
	char buf[2];
	buf[0] = reg;
	buf[1] = val;
	client = chip->client;
	ret = i2c_master_send(client, buf, 2);
	if(ret < 2){
		dev_err(&client->dev, "%s error: %d\n", __func__, ret);
	}
	return ret;
}

static int sn732x_readb(struct sn732x_chip *chip, uint8_t reg, uint8_t *val)
{
	struct i2c_client *client = chip->client;
	int ret = 0;
    struct i2c_msg msgs[] = {
        {
            .addr   = client->addr,
            .flags  = client->flags,
            .len    = 1,
            .buf    = &reg,
            .scl_rate = SN7325_I2C_SPEED,  // add for rk312x
        },
        {
            .addr   = client->addr,
            .flags  = client->flags |I2C_M_RD,
            .len    = 1,
            .buf    = val,
            .scl_rate = SN7325_I2C_SPEED,   // add for rk312x
        },
    };

    ret = i2c_transfer(client->adapter, msgs, 2);  // 这里 num = 2,通信成功 ret = 2
    if (ret < 0)
        dev_err(&client->dev, "%s error: %d\n", __func__, ret);
    return ret;

}

static int sn732x_gpio_get_value(struct gpio_chip *gc, unsigned off)
{
	struct sn732x_chip *chip = to_sn732x(gc);
	struct i2c_client *client = chip->client;
	int ret = 0;
	int bank = off / 8;
	u8 dat;
	dev_info(&client->dev, "%s off=%u\n", __func__, off);
	ret = sn732x_readb(chip, bank + 0, &dat);
	if(ret > 0){
		chip->reg_in[bank] = dat;
		return dat >> (off %8) & 0x1;
	}else{
		return ret;
	}
}

static void sn732x_gpio_set_value(struct gpio_chip *gc, unsigned off, int val)
{
	struct sn732x_chip *chip = to_sn732x(gc);
	struct i2c_client *client = chip->client;
	int bank = off / 8;
	u8 conf = chip->reg_out[bank];
	u8 rb = 0;
	dev_info(&client->dev, "%s off=%d, val=%d\n", __func__, off, val);
	if(val){
		conf |= (1<< (off % 8)); // set to 1
	}else{
		conf &= ~(1<< (off % 8)); // set to 0
	}
	sn732x_writeb(chip, 2+bank, conf);
	if(debug_enable){
		sn732x_readb(chip, 2+bank, &rb);
		dev_info(&client->dev, "%s write 0x%x, read 0x%x\n", __func__, conf, rb);
	}
	chip->reg_out[bank] = conf;
}
#if 0
static void sn732x_gpio_set_multiple(struct gpio_chip *gc,
				      unsigned long *mask, unsigned long *bits)
{
	unsigned mask_lo = mask[0] & 0xff;
	unsigned mask_hi = (mask[0] >> 8) & 0xff;

	if (mask_lo)
		sn732x_gpio_set_mask(gc, 0, mask_lo, bits[0] & 0xff);
	if (mask_hi)
		sn732x_gpio_set_mask(gc, 8, mask_hi, (bits[0] >> 8) & 0xff);
}
#endif
static int sn732x_gpio_direction_input(struct gpio_chip *gc, unsigned off)
{
	struct sn732x_chip *chip = to_sn732x(gc);
	struct i2c_client *client = chip->client;
	int bank = off / 8;
	u8 rb = 0;
	u8 conf = chip->reg_conf[bank];
	dev_info(&client->dev, "%s off=%d\n", __func__, off);
	conf |= (1<< (off % 8)); // set to 1
	sn732x_writeb(chip, 4+bank, conf);
	chip->reg_conf[bank] = conf;
	if(debug_enable){
		sn732x_readb(chip, 4+bank, &rb);
		dev_info(&client->dev, "%s write 0x%x, read 0x%x\n", __func__, conf, rb);
	}
	return 0;
}

static int sn732x_gpio_direction_output(struct gpio_chip *gc,
		unsigned off, int val)
{
	struct sn732x_chip *chip = to_sn732x(gc);
	struct i2c_client *client = chip->client;
	int bank = off / 8;
	u8 conf = chip->reg_conf[bank];
	dev_info(&client->dev, "%s off=%d, val=%d\n", __func__, off, val);
	conf &= ~(1<< (off % 8)); // set to 0
	sn732x_writeb(chip, 2+bank, conf);
	chip->reg_conf[bank] = conf;
	sn732x_gpio_set_value(gc, off, val);
	return 0;
}


static int sn732x_irq_setup(struct sn732x_chip *chip,
			     const struct i2c_device_id *id)
{
	return 0;
}

static int sn732x_setup_gpio(struct sn732x_chip *chip, struct device *dev)
{
	struct gpio_chip *gc = &chip->gpio_chip;
	chip->dir_input = chip->dir_output = 1;
	gc->direction_input = sn732x_gpio_direction_input;
	gc->direction_output = sn732x_gpio_direction_output;
	gc->set = sn732x_gpio_set_value;
	gc->get = sn732x_gpio_get_value;
	//	gc->set_multiple = sn732x_gpio_set_multiple;
	gc->get = sn732x_gpio_get_value;
	gc->can_sleep = 1;
	gc->base = -1;
	gc->ngpio = 16;
	gc->label = chip->client->name;
//	gc->parent = &chip->client->dev;
	gc->dev = dev;
	gc->of_node = dev->of_node;
	gc->owner = THIS_MODULE;

	return 0;
}

static struct sn732x_platform_data *of_gpio_sn732x(struct device *dev)
{
	struct sn732x_platform_data *pdata;

	pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL);
	if (!pdata)
		return NULL;
	pdata->gpio_base = -1;  // dynamic allocate gpio_base
	return pdata;
}

static ssize_t sn732x_show(struct device *dev,struct device_attribute *attr, char *buf)
{
	int n=0, i=0;
	u8 val;
	struct sn732x_chip * chip = dev_get_drvdata(dev);
	dev_info(dev, "%s\n", __func__);
	n = sprintf(buf, "sn7325 dump\n");
	for(i=0; i<8; i++){
		sn732x_readb(chip, i, &val);
		n += sprintf(buf+n, "reg %d: 0x%x\n",i, val);
	}
	return n;
}
static DEVICE_ATTR(info, 0444, sn732x_show, NULL);

static int sn732x_probe(struct i2c_client *client,
				   const struct i2c_device_id *id)
{
	struct sn732x_platform_data *pdata;
	struct device_node *node;
	struct sn732x_chip *chip;
	int ret, nr_port;

	pdata = dev_get_platdata(&client->dev);
	node = client->dev.of_node;

	if (!pdata && node)
		pdata = of_gpio_sn732x(&client->dev);

	if (!pdata) {
		dev_dbg(&client->dev, "no platform data\n");
		return -EINVAL;
	}

	chip = devm_kzalloc(&client->dev, sizeof(*chip), GFP_KERNEL);
	if (chip == NULL)
		return -ENOMEM;
	chip->client = client;
	nr_port = sn732x_setup_gpio(chip, &client->dev);
	//chip->gpio_chip.parent = &client->dev;
	mutex_init(&chip->lock);

	ret = gpiochip_add(&chip->gpio_chip);
	if (ret)
		goto out_failed;

	ret = sn732x_irq_setup(chip, id);
	if (ret) {
		ret = gpiochip_remove(&chip->gpio_chip);
		if(ret < 0){
			dev_err(&client->dev, "%s failed, %d\n", "gpiochip_remove", ret);
		}
		goto out_failed;
	}

	if (pdata && pdata->setup) {
		ret = pdata->setup(client, chip->gpio_chip.base,
				chip->gpio_chip.ngpio, pdata->context);
		if (ret < 0)
			dev_warn(&client->dev, "setup failed, %d\n", ret);
	}

	i2c_set_clientdata(client, chip);
	ret = device_create_file(&client->dev, &dev_attr_info);
	if(ret < 0){
		dev_err(&client->dev, "device_create_file failed ret=%d\n", ret);
	}
	return 0;

out_failed:
	return ret;
}

static int sn732x_remove(struct i2c_client *client)
{
	int ret = 0;
	struct sn732x_platform_data *pdata = dev_get_platdata(&client->dev);
	struct sn732x_chip *chip = i2c_get_clientdata(client);
	device_remove_file(&client->dev, &dev_attr_info);
	if (pdata && pdata->teardown) {

		ret = pdata->teardown(client, chip->gpio_chip.base,
				chip->gpio_chip.ngpio, pdata->context);
		if (ret < 0) {
			dev_err(&client->dev, "%s failed, %d\n", "teardown", ret);
			return ret;
		}
	}

	ret = gpiochip_remove(&chip->gpio_chip);
	if(ret < 0){
		dev_err(&client->dev, "%s failed, %d\n", "gpiochip_remove", ret);
	}
	return 0;
}

static struct i2c_driver sn732x_driver = {
	.driver = {
		.name		= "sn732x",
		.owner		= THIS_MODULE,
		.of_match_table	= of_match_ptr(sn732x_of_table),
	},
	.probe		= sn732x_probe,
	.remove		= sn732x_remove,
	.id_table	= sn732x_id,
};

static int __init sn732x_init(void)
{
	return i2c_add_driver(&sn732x_driver);
}
/* register after i2c postcore initcall and before
 * subsys initcalls that may rely on these GPIOs
 */
#if 1
subsys_initcall_sync(sn732x_init);
#else
module_init(sn732x_init);
#endif
static void __exit sn732x_exit(void)
{
	i2c_del_driver(&sn732x_driver);
}
module_exit(sn732x_exit);

MODULE_AUTHOR("Eric Miao <eric.miao@marvell.com>");
MODULE_DESCRIPTION("GPIO expander driver for SN732X");
MODULE_LICENSE("GPL");
